/*
 * tarm_iic.c
 *
 *  Created on: 2019年12月10日
 *      Author: tinnu
 */

#include "my_sys.h"

#include "tarm_iic.h"
#include "tarm_pca9685.h"


#define PCA9685_device7byteAddress 0x40

#define PCA9685_SUBADR1 0x2
#define PCA9685_SUBADR2 0x3
#define PCA9685_SUBADR3 0x4

#define PCA9685_MODE1 0x0
#define PCA9685_PRESCALE 0xFE

#define LED0_ON_L 0x6
#define LED0_ON_H 0x7
#define LED0_OFF_L 0x8
#define LED0_OFF_H 0x9

#define ALLLED_ON_L 0xFA
#define ALLLED_ON_H 0xFB
#define ALLLED_OFF_L 0xFC
#define ALLLED_OFF_H 0xFD

int PCA9685_WriteAByte(uint8_t i_dateAddr, uint8_t i_byte)
{
	return IIC_sendOneByte(PCA9685_device7byteAddress, i_dateAddr, i_byte);
}

uint8_t PCA9685_ReadAByte(uint8_t i_dateAddr, int *o_returnValue)
{
	uint8_t t_byte=0;
	t_byte = IIC_ReadOneByte(PCA9685_device7byteAddress, i_dateAddr);
	*o_returnValue=0;
	return t_byte;
}

int pca9685_init(void)	//因为可以多个pca9685级联,此驱动仅作为驱动舵机使用，周期固定死位20ms，不允许外部设置
{
	//初始化pca9685芯片
	double T = 20000;	//周期，单位是us
	unsigned char prescale;
	double osc_clock = 25000000;
	unsigned char oldmode, newmode;
	int t_returnCal=0;
	T /= 0.915;	//不知道为什么，会有所偏差，这里校准一下就ok了，从网上找的arduino代码也进行了校准。
	T /= 1000000;	//把T转换成秒
	prescale = (unsigned char)(osc_clock / 4096 * T - 1);
	//	printf("prescale = 0x%x", prescale);
	PCA9685_WriteAByte(PCA9685_MODE1, 0x0);
	oldmode = PCA9685_ReadAByte(PCA9685_MODE1, &t_returnCal);
	newmode = (oldmode & 0x7f) | 0x10;

	//准备进入sleep，设置时钟前必须先进入sleep模式
	PCA9685_WriteAByte(PCA9685_MODE1, newmode);
	PCA9685_WriteAByte(PCA9685_PRESCALE, prescale);
	oldmode &= 0xef;	//清除sleep位
	PCA9685_WriteAByte(PCA9685_MODE1, oldmode);
	delay_ms(1);
	PCA9685_WriteAByte(PCA9685_MODE1, oldmode | 0xa1);
	return 0;
}

void setPWMFreq(float freq)
{
	uint32_t prescale,oldmode,newmode;
	float prescaleval;
	int t_returnCal=0;
	freq *= 0.92f;  // Correct for overshoot in the frequency setting
	prescaleval = 25000000;
	prescaleval /= 4096;
	prescaleval /= freq;
	prescaleval -= 1;
	prescale = (uint32_t)(prescaleval + 0.5f);

	PCA9685_WriteAByte(PCA9685_MODE1, 0x0);
	oldmode = PCA9685_ReadAByte(PCA9685_MODE1, &t_returnCal);
	newmode = (oldmode&0x7F) | 0x10; // sleep
	PCA9685_WriteAByte(PCA9685_MODE1, newmode); // go to sleep
	PCA9685_WriteAByte(PCA9685_PRESCALE, prescale); // set the prescaler
	PCA9685_WriteAByte(PCA9685_MODE1, oldmode);
	delay_ms(2);
	PCA9685_WriteAByte(PCA9685_MODE1, oldmode | 0xa1);
}

void setPWM(uint32_t num, uint32_t on, uint32_t off)
{
	PCA9685_WriteAByte(LED0_ON_L+4*num,on);
	PCA9685_WriteAByte(LED0_ON_H+4*num,on>>8);
	PCA9685_WriteAByte(LED0_OFF_L+4*num,off);
	PCA9685_WriteAByte(LED0_OFF_H+4*num,off>>8);
}

void pca9685_setmk(int num, int mk)	//设置指定通道的脉宽。fd是在pca9685_init时获得的设备描述符，num是通道号（从0开始），mk是脉宽单位是us。周期已经固定为20ms了
{
	uint32_t ON, OFF;
	ON = 0;	//每次周期一开始就输出高电平
	OFF = (unsigned int)((((double)mk) / 20000 * 4096)*1.0067114);	//最后的1.0067114是校准用的
	//    printf("off = 0x%x", OFF);

	PCA9685_WriteAByte(LED0_ON_L + 4 * num, ON);
	PCA9685_WriteAByte(LED0_ON_H + 4 * num, ON>>8);
	PCA9685_WriteAByte(LED0_OFF_L + 4 * num, OFF);
	PCA9685_WriteAByte(LED0_OFF_H + 4 * num, OFF>>8);
}

void PCA9685_Reset(void)
{
	PCA9685_WriteAByte(PCA9685_MODE1, 0x0);
}
